﻿#include "ui/window.h"
#include "ui/util.h"
#include "texture.h"
#include "context.h"
#include "draw.h"

using namespace std;
using namespace anip;

ui::Window::Window(Container* p, invec2 size)
{
	if (p != nullptr)
	{
		_margin = { 0, r::DockedWindow_HeaderSize };
		_font.size = (int)(r::DockedWindow_HeaderFontSize);
	}

	if (p == nullptr)
		_size = Context::size();
	else
	{
		_size = size;
		p->addChild(*this, true);
	}

	//applyTheme(*ui::Theme::current);
}

void ui::Window::processOnDocked()
{
	_undockSize = _size;
	if (isTabbed())
		_margin = { 0, 0 };
	else
		_margin = { 0, r::DockedWindow_HeaderSize };
	_font.size = (int)(r::DockedWindow_HeaderFontSize);
	setFont(_font);
}

void ui::Window::processOnUndocked()
{
	setSize(_undockSize);
	_margin = { 0, r::DockedWindow_HeaderSize };
	_font.size = (int)(r::DockedWindow_HeaderFontSize);
	setFont(_font);
}

void ui::Window::processMouseMove(invec2 pos)
{
	Context::setCursor(KnownCursor::Arrow);

	if (_parent != nullptr)
	{
		if (allowResize && !isDocked())
		{
			// ANIP_FIXME: there isn't any diagonal resize cursor!
			bool 
				h = pos.x < 3 || pos.x > _size.x - r::Window_ResizeZoneSize, 
				v = pos.y < 3 || pos.y > _size.y - r::Window_ResizeZoneSize;
			if (h && v)
				Context::setCursor(KnownCursor::Crosshair);
			else if (h)
				Context::setCursor(KnownCursor::HResize);
			else if (v)
				Context::setCursor(KnownCursor::VResize);
		}

		float dist = r::Window_ControlBtn_Distance;
		vec2 p{ _size.x - dist, _margin.y * 0.5f };
		for (auto& btn : controlButtons)
		{
			bool h = math::distance(pos, p) < r::Window_ControlBtn_Size * 0.5f;
			if (btn._hover != h) 
			{
				btn._hover = h;
				postSignal({ ui::Signal::Type::NeedsRedraw }); 
			}
			p -= vec2{ dist, 0 };
		}
	}

	if (pos.y > _margin.y)
		Control::processMouseMove(pos);
	else
		forceDehoverChildren();
}

void ui::Window::processMouseButton(MouseButton btn, ButtonAction action)
{
	if (action == ButtonAction::Down)
		for (auto& btn : controlButtons)
		{
			if (btn._hover)
			{
				postSignal({ ui::Signal::Type::NeedsRedraw });
				btn._press = true;
			}
		}
	else if (action == ButtonAction::Up)
		for (auto& btn : controlButtons)
			if (btn._press)
			{
				postSignal({ ui::Signal::Type::NeedsRedraw });
				if (btn.onClick) btn.onClick();
				btn._press = false;
			}

	Control::processMouseButton(btn, action);
}

void ui::Window::processFocus()
{
	postSignal({ ui::Signal::Type::NeedsRedraw });
	bringToFront();
	Control::processFocus();
}

void ui::Window::processDefocus()
{
	postSignal({ ui::Signal::Type::NeedsRedraw });
	Control::processDefocus();
}

bool ui::Window::canBeginDrag(MouseButton btn, invec2 pos)
{
	_dragType = DragType::None;

	if (_parent != nullptr && allowResize && !isDocked())
	{
		_oldSize = _size;
		if (pos.x < r::Window_ResizeZoneSize)
			_dragType |= DragType::Left;
		else if (pos.x > _size.x - r::Window_ResizeZoneSize)
			_dragType |= DragType::Right;

		if (pos.y < r::Window_ResizeZoneSize)
			_dragType |= DragType::Top;
		else if (pos.y > _size.y - r::Window_ResizeZoneSize)
			_dragType |= DragType::Bottom;
	}
	if (_dragType == DragType::None && pos.y < _margin.y)
		_dragType |= DragType::Move;
	//if (_dragType != DragType::None)
	//	cout << "Window drag " << _dragType.to_bitset().to_ulong() << endl;
	return _dragType != DragType::None;
}

void ui::Window::processDrag(invec2 start, invec2 pos)
{
	if (isDocked())
	{
		if (math::length(pos - start) > r::Autodock_DetachDistance)
		{
			dockContainer()->detachChild(*this);
			topContainer()->addChild(*this, true);
		}
		else
			return;
	}

	if (_dragType & DragType::Move)
	{
		if (Context::holdingControl())
		{
			Control* over = topContainer();
			AutoDockContainer* adc = nullptr;
			while (over != nullptr)
			{
				if (typeid(*over) == typeid(AutoDockContainer))
				{
					adc = (AutoDockContainer*)over;
					break;
				}
				over = ((Container*)over)->draggedOverChild();
			}

			if (adc != nullptr)
				adc->setDockHint(adc->toLocalPos(Context::mousePos()));
		}
		setPosition(_pos + pos - start);
		return;
	}

	bool invert = false; vec2 factor{};
	if (_dragType & DragType::Left || _dragType & DragType::Top)
		invert = true;
	if (_dragType & DragType::Left || _dragType & DragType::Right)
		factor.x = 1;
	if (_dragType & DragType::Top || _dragType & DragType::Bottom)
		factor.y = 1;

	vec2 delta = (pos - start) * factor;

	if (invert)
	{
		setPosition(_pos + delta);
		setSize(_size - delta);
	}
	else setSize(_oldSize + delta);
}

void ui::Window::processEndDrag()
{
	if (_dragType & DragType::Move && Context::holdingControl())
	{
		Control* over = topContainer();
		AutoDockContainer* adc = nullptr;
		while (over != nullptr)
		{
			if (typeid(*over) == typeid(AutoDockContainer))
			{
				adc = (AutoDockContainer*)over;
				break;
			}
			over = ((Container*)over)->draggedOverChild();
		}
		if (adc != nullptr) adc->dockControl(*this, 
			adc->setDockHint(adc->toLocalPos(Context::mousePos())));
	}
}

void ui::Window::setPosition(invec2 pos)
{
	if (this == topContainer())
	{
		Context::setPosition(pos);
		Control::setPosition({ 0, 0 });
		_pos = pos;
	}
	else
	{
		Control::setPosition(pos);
		if (_parent != nullptr)
			_parent->postSignal({ ui::Signal::Type::UpdateScrollableArea });
	}
}

void ui::Window::setSize(invec2 size)
{
	if (size == _size) return;

	if (this == topContainer())
		Context::setSize(size * Control::UIScaling());
	else if (_parent != nullptr)
		_parent->postSignal({ ui::Signal::Type::UpdateScrollableArea });

	Control::setSize(size);
	postSignal({ Signal::Type::NeedsReflow });
}

void ui::Window::setText(const wstring& f)
{
	if (this == topContainer())
		Context::setTitle(f);
	WithText::setText(f);
}

ui::Window::ControlButton& ui::Window::addCloseButton()
{
	postSignal({ ui::Signal::Type::NeedsRedraw });
	controlButtons.push_back({ r::Window_CloseButtonColor });
	auto& btn = controlButtons[controlButtons.size() - 1];
	btn.onClick = [this]()
	{
		markDelete();
	};
	return btn;
}

void _debugRect(vec2 pos, vec2 size, color col, bool dash = false)
{
	float du = dash ? 10.0f : 1.0f;
	float ds = dash ? 5.0f : 1.0f;
	Draw::DashOption option;
	option.unitLength = du;
	option.solidLength = ds;
	Draw::line(
		{ pos.x - 10, pos.y },
		{ pos.x + size.x + 10, pos.y },
		2, col, option);
	Draw::line(
		{ pos.x - 10, pos.y + size.y },
		{ pos.x + size.x + 10, pos.y + size.y },
		2, col, option);
	Draw::line(
		{ pos.x, pos.y - 10 },
		{ pos.x, pos.y + size.y + 10 },
		2, col, option);
	Draw::line(
		{ pos.x + size.x, pos.y - 10 },
		{ pos.x + size.x, pos.y + size.y + 10 },
		2, col, option);
}

void ui::Window::paint()
{
	if (_parent != nullptr)
	{
		// shadow
		if (!isDocked())
		{
			float size = _focus 
				? r::Window_Focused_ShadowSize 
				: r::Window_Unfocused_ShadowSize;
			color col = _focus 
				? r::Window_Focused_ShadowColor 
				: r::Window_Unfocused_ShadowColor;
			Draw::shadowRect(_absPos + vec2{ 0, size * 0.4f }, _size, col, size);
		}

		// header + background
		{
			vec2 headSize{ _size.x, _margin.y };
			if (isDocked())
			{
				Draw::rect(_absPos, _size, backColor);
				Draw::rect(_absPos, headSize, headerColor);
			}
			else
			{
				Draw::roundedRect(_absPos, _size, 4, backColor);
				Draw::roundedRect(_absPos, headSize, 4, headerColor);
				Draw::rect(
					_absPos + vec2{ 0, 4 },
					vec2{ _size.x, _margin.y - 4 },
					headerColor);
			}
			if (!isTabbed())
			{
				auto textTex = getTextTexture();
				if (textTex != nullptr)
				{
					vec2 tsize = textTex->size();
					Draw::text(_absPos + (headSize - tsize) * vec2 { 0, 0.5 } + vec2{ 16, 0 },
						tsize, * textTex, textColor);
				}
			}
		}

		// control buttons
		if (!isTabbed())
		{
			float dist = r::Window_ControlBtn_Distance;
			float size = r::Window_ControlBtn_Size;
			vec2 p = _absPos + vec2{ _size.x - dist, _margin.y * 0.5f };
			vec2 s{ size, size };
			for (auto& btn : controlButtons)
			{
				color c = btn.backColor;
				if (btn._hover) c = c.mulWithoutAlpha(r::Button_HoverFactor);
				if (btn._press) c = c.mulWithoutAlpha(r::Button_PressFactor);
				Draw::ellipse(p - s * 0.5f, s, c);

				if (btn.texture != nullptr)
				{
					vec2 t = s / btn.texture->size();
					vec2 scaled = btn.texture->size() * min(t.x, t.y);
					Draw::texture(p - scaled * 0.5f, scaled, *btn.texture);
				}

				p -= vec2{ dist, 0 };
			}
		}
	}

	AutoDockable::paint();

	// contents
	Context::addScissor(_absPos + _margin, _size - _margin);
	vec2 sp, ss;
	Context::getScissor(sp, ss);
	for (Control* child = lastChild(); child != nullptr; child = child->previous())
	{
		Context::setScissor(sp, ss);
		child->paint();
	}
	drawScrollers();
	Context::noScissor();

#ifndef UINDEBUG
	const color debugLineColor = Colors::navy * color{ 1, 1, 1, 0.6f };
	const color debugLineColorActive = Colors::maroon * color{ 1, 1, 1, 0.6f };

	if (focused()) for (Control* child = lastChild(); child != nullptr; child = child->previous())
	{
		color col = child->focused() ? debugLineColorActive : debugLineColor;

		vec2 pos, size;
		if (child->margin() != vec2{ 0 } || child->marginBottom() != vec2{ 0 })
		{
			pos = child->toAbsolutePos({ 0, 0 }) - child->margin();
			size = child->size() ;
			_debugRect(pos, size, col, true);
		}

		pos = child->toAbsolutePos({ 0, 0 });
		size = child->size() - child->margin() - child->marginBottom();
		_debugRect(pos, size, col);
	}
#endif
}